from none_bot_py_plugins.net.net import *
from none_bot_py_plugins.net.bot_recv import *
from none_bot_py_plugins.mod.mod_handler import mod_handler

min_port = 12000
max_port = 14000
now_port = 12000
port_list = list()
port_map = {}
host_address = '127.0.0.1'
server_map = {}


class ServerLoop:
    def __init__(self, uuid, loop, connect_type, conn_id):
        self.__loop = loop
        self.__protocol = None
        self.__transport = None
        self.__port = 0
        self.__uuid = uuid
        self.__connect_type = connect_type
        self.__connect_id = conn_id
        self.player = None

    def start(self):
        asyncio.set_event_loop(self.__loop)
        self.__loop.run_forever()

    async def stop(self):
        self.ret_client("已退出！")
        self.__loop.stop()

    async def __create_connect(self):
        global min_port
        global max_port
        global now_port
        global port_list
        global port_map
        global host_address
        global server_map
        port = 0
        if len(port_list) == 0:
            if now_port + 1 < max_port:
                port = now_port
                now_port = now_port + 1
        else:
            port = port_list[0]
            port_list = port_list[1, len(port_list)]
        if port != 0:
            transport, protocol = await self.__loop.create_connection(
                lambda: NetHolder(self.__uuid),
                host_address, 9088)
            self.__protocol = protocol
            self.__transport = transport
            self.__port = port
            port_map[self.__uuid] = port
            server_map[self.__uuid] = self
            return True
        else:
            return False

    async def __disconnect(self):
        global port_list
        global port_map
        global server_map
        self.__protocol = None
        self.ret_client("已断开连接")
        self.__connect_type = None
        self.__connect_id = 0
        try:
            self.__transport.close()
        finally:
            self.__transport = None
        if self.__uuid in port_map:
            port_list.append(port_map[self.__uuid])
            del port_map[self.__uuid]
            del server_map[self.__uuid]
            self.__port = 0
        return True

    async def __send_msg(self, proto_object):
        self.__protocol.pack_send(proto_object)

    async def __recv_msg(self, proto_object):
        mod_handler(self, proto_object)

    async def __on_doing(self, fun, args, kwargs):
        fun(self.player, *args, **kwargs)

    def run_task(self, command, msg=None, fun=None):
        try:
            if command == 'connect':
                asyncio.run_coroutine_threadsafe(self.__create_connect(), self.__loop)
            elif command == 'disconnect':
                asyncio.run_coroutine_threadsafe(self.__disconnect(), self.__loop)
            elif command == 'send_info':
                asyncio.run_coroutine_threadsafe(self.__send_msg(msg), self.__loop)
            elif command == 'recv_info':
                asyncio.run_coroutine_threadsafe(self.__recv_msg(msg), self.__loop)
            elif command == 'on_doing':
                args, kwargs = msg
                asyncio.run_coroutine_threadsafe(self.__on_doing(fun, args, kwargs), self.__loop)
            elif command == 'stop':
                asyncio.run_coroutine_threadsafe(self.stop(), self.__loop)
            else:
                pass
        except Exception:
            pass
        return True

    def ret_client(self, msg):
        bot_recv(self.__connect_type, self.__connect_id, self.__uuid, msg)


def cmd_server(uuid, command, msg=None, fun=None):
    global server_map
    if uuid in server_map:
        server_map[uuid].run_task(command, msg, fun)
    else:
        pass
    return True


def send_info(qq_id, msg):
    cmd_server(qq_id, 'send_info', msg)


def recv_info(qq_id, obj):
    cmd_server(qq_id, 'recv_info', obj)


def connect(qq_id):
    cmd_server(qq_id, 'connect')


def stop(qq_id):
    cmd_server(qq_id, 'stop')


def disconnect(qq_id):
    cmd_server(qq_id, 'disconnect')


# 执行装饰器
# 第一个参数为player
# 最后一个参数为qq_id
# 被装饰的函数执行的时候会被抛到角色线程去执行
def on_doing(callback):
    def f(*args, **kwargs):
        uuid = kwargs['qq_id']
        cmd_server(uuid, 'on_doing', (args, kwargs), callback)
    return f
